
#include "ComUtils.h"
#include "ErrorCode.h"
#include "EvURI.h"
#include <event2/util.h>
#include <event2/http.h>

std::string addr_to_ipport(const sockaddr* addr)
{
    char str_addr[64] = {};
    int port = 0;
    if (addr->sa_family == AF_INET6) {
        sockaddr_in6 *addr4 = (sockaddr_in6*)addr;
        evutil_inet_ntop(AF_INET6, &addr4->sin6_addr, str_addr, sizeof(str_addr));
        port = ntohs(addr4->sin6_port);
    }
    else if (addr->sa_family == AF_INET) {
        sockaddr_in *addr6 = (sockaddr_in*)addr;
        evutil_inet_ntop(AF_INET6, &addr6->sin_addr, str_addr, sizeof(str_addr));
        port = ntohs(addr6->sin_port);
    }
    else {
        return "";
    }
    return str_addr + std::string(":") + std::to_string(port);
}

std::error_code dns_resolve(const std::string& host, int port, std::vector<struct sockaddr_storage>& addr)
{
    struct addrinfo hints;
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC; /* v4 or v6 is fine. */
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP; /* We want a TCP socket */
    hints.ai_flags = AI_PASSIVE;    /* For wildcard IP address */

                                    /* Look up the hostname. */
    struct addrinfo* answer = nullptr;
    int err = evutil_getaddrinfo(host.c_str(), nullptr, &hints, &answer);
    if (err != 0) {
        return make_socket_error_code(EVUTIL_SOCKET_ERROR());
    }

    for (struct addrinfo* rp = answer; rp != nullptr; rp = rp->ai_next)
    {
        struct sockaddr_storage saddr = {};
        if (rp->ai_family == AF_INET)
        {
            struct sockaddr_in* a = (sockaddr_in*)(rp->ai_addr);
            if (a->sin_addr.s_addr == 0) {
                continue;
            }
            memcpy(&saddr, a, sizeof(*a));
            ((sockaddr_in&)saddr).sin_port = htons(port);
            addr.emplace_back(saddr);
        }
        if (rp->ai_family == AF_INET6)
        {
            struct sockaddr_in6* a = (sockaddr_in6*)(rp->ai_addr);
            memcpy(&saddr, a, sizeof(*a));
            ((sockaddr_in6&)saddr).sin6_port = htons(port);
            addr.emplace_back(saddr);
        }
    }

    evutil_freeaddrinfo(answer);
    return std::error_code();
}

std::error_code uri_dns_resolve(const EvURI& uri, sockaddr_storage& endpoint)
{  
    if (uri.port() == 0 || uri.host().empty()) {
        return std::make_error_code(std::errc::invalid_argument);
    }
    std::vector<sockaddr_storage> vaddr;
    auto ec = dns_resolve(uri.host(), uri.port(), vaddr);
    if (ec) {
        return ec;
    }
    if (vaddr.empty()) {
        return make_custom_error_code(custom_error::dns_resolve_error);
    }
    endpoint = vaddr[0];
    return std::error_code();
}

